package com.example.securitydemo.filter;

import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import com.example.securitydemo.util.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class JwtRedisValidateFilter extends OncePerRequestFilter {

    @Resource
    private RedisTemplate redisTemplate;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 差看是否有token
        String token = request.getHeader("token");
        // 获取请求的路径是什么
        String requestURI = request.getRequestURI();
        //
       // if(requestURI.equals("/logout")){

           //UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, null, collect);
            //SecurityContextHolder.getContext().setAuthentication(null);
       // filterChain.doFilter(request, response);
    //}else
        if(requestURI.equals("/usrlogin")||requestURI.equals("/logOut")) {
           filterChain.doFilter(request, response);
        } else {

            //
            if (StringUtils.isNotBlank(token)) {
                // 校验token
                // redis中取出token对应的value的值
                Object o = redisTemplate.opsForValue().get(token);
                if (o == null) {
                    // 可能当前的token已经过期了
                    // 刷新的token在不在
                    Object o1 = redisTemplate.opsForValue().get("REFRESH_" + token);
                    // 刷新的token不存在
                    if (o1 == null) {
                        Result result = new Result(420, "token过期", null);
                        // 生成一个新的token
                        // 如何传递到前台
                        // user
                        printJsonData(result, response);
                    } else {
                        // 刷新的token在
                        redisTemplate.opsForValue().set(token, o1.toString(), 3, TimeUnit.MINUTES);
                        parseTokenAndSetUserMsg(true, token, o1, request, response, filterChain);
                    }

                } else {
                    // token 在有有效期之内
                    parseTokenAndSetUserMsg(false, token, o, request, response, filterChain);
                }


            } else {
                // 没有
                // a. 访问的是登录的路径的时候放行
                if (requestURI.equals("/usrlogin")) {
                    filterChain.doFilter(request, response);
                } else {
                    // 返回json数据 没有登录
                    Result result = new Result(400, "没有登录", null);
                    printJsonData(result, response);
                }

            }
        }
    }

    /**
     * 生成新的token
     */
    public void getRefreshToken(String username,List<String> authorities,String token){

        // 重新生成一个refreshtoken的值  存放到redis里面  REFRESH_
        Map map1 = new HashMap();

        map1.put("username",username);

        map1.put("authorities",authorities);

//            map.put("name","yyl");
//            map.put("pwd","123456");
        // 有效期 颁发 30 分钟
        Calendar calendar1 = Calendar.getInstance();
        Date time1 = calendar1.getTime();


        calendar1.add(Calendar.MINUTE,3*2); // 30   10  表单 15分钟
        long expiretime1 = calendar1.getTime().getTime(); // 时间的毫秒数

        // 过期时间
        map1.put(JWTPayload.EXPIRES_AT,expiretime1);
        // 签发的时间
        map1.put(JWTPayload.ISSUED_AT,time1.getTime());
        // 生效的时间
        map1.put(JWTPayload.NOT_BEFORE,time1.getTime());

        String refreshtoken = JWTUtil.createToken(map1, "yyl".getBytes());

        //存到redis里面   token

        // 续期的token存起来
        redisTemplate.opsForValue().set("REFRESH_"+token,refreshtoken,3*2, TimeUnit.MINUTES);
    }

    /**
     * 解析token 并设置用户的信息
     * @param o
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */

    public void parseTokenAndSetUserMsg(Boolean refreshVariable,String token, Object o, HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // token 在有效期之内
        // 解析token username  资源
        String token1 = o.toString();
        boolean verify = JWTUtil.verify(token1, "yyl".getBytes()); // 解析token 是不是自己颁发的
        if (verify){
            // 解析token
            JWT jwt = JWTUtil.parseToken(token1);

            // 荷载的信息   username
            String username = jwt.getPayload().getClaim("username").toString();
            // 资源的信息
            List<String> authorities = (List<String>) jwt.getPayload().getClaim("authorities");
            List<SimpleGrantedAuthority> collect = authorities.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList());
            //如果刷新token的值有 重新获取刷新token的值并保存到redis里面
            if(refreshVariable) {
                getRefreshToken(username, authorities, token);
            }


            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, null, collect);
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            filterChain.doFilter(request, response);

        }else{
            Result result = new Result(410,"token无效",null);
            printJsonData(result,response);
        }
    }

    public void printJsonData(Result result,HttpServletResponse response) throws IOException {
        // 以json的形式 传递除去
        ObjectMapper objectMapper = new ObjectMapper();
        String s = objectMapper.writeValueAsString(result);
        // 响应到前端
        response.setContentType("application/json;charset=utf8");
        PrintWriter writer = response.getWriter();
        writer.write(s);
        writer.flush();
        writer.close();
    }
}
